/**
 * Copyright (C) Sarah Mount, 2008.
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 **/
includes SampleMsg;
module DingoBoomM
{
  provides interface StdControl;
  uses {
    interface Timer as SampleTimer;
    interface Leds;								// LEDs
    interface ADC as Photo;						// Light meter
    interface SplitControl as AccelControl;		// Accelerometer
    interface ADC as AccelX;
    interface ADC as AccelY;
    interface MSP430Interrupt as Button;		// User button
	interface ADC as InternalTemp;				// Temperature sensor
	interface ADC as InternalVoltage;			// Voltameter
	interface ADC as Microphone;				// Microphone
    interface StdControl as CommControl;		// Communications
    interface SendMsg as SendSampleMsg;
  }
}

implementation {
  
  enum {
  	READY,
   	NOT_READY // Can't use BUSY here
  };
  
  enum {
    TIMER_RATE = 20/BUFFER_SIZE
  };
  
  typedef struct Sample {
  	bool button;
  	uint16_t reading; // Use for accelX if channel is SENSE_ACCEL
  	uint16_t accelY;
  	enum modality channel;
  }Sample;
  
  uint8_t state; // READY or NOT_READY
  
  // Whether or not we're sampling the different modalities
  bool sampleLight   = TRUE;
  bool sampleAccel   = TRUE;
  bool sampleButton  = TRUE;
  bool sampleTemp    = TRUE;
  bool sampleVoltage = TRUE;
  bool sampleSound   = TRUE;

  // Samples from ADCs and related data
  uint8_t nextSample;
  uint16_t accelX;
  Sample samples[BUFFER_SIZE];

  // Messages
  uint8_t nextMsg;
  struct SampleMsg *am; // pointer to payload in either msg[0] or msg[1]
  TOS_Msg msg[2];       // Message queue
  
  /* Send radio message ********************************************************/
  task void makeSendMsg() {
    uint8_t i, lastMsg, sampleCtr; // we need this to use last data mesg.
    struct SampleMsg *_am; // temp payload pointer (for last msg)
	uint16_t reading;
	
	atomic sampleCtr = nextSample;
	
    for(i=0; i<sampleCtr; i++) {
    	atomic lastMsg = nextMsg;
    	lastMsg ^= 0x1; // use last buffer rather than current buffer
    	_am = (struct SampleMsg *)(msg[lastMsg].data); // point to last msg (buffer is being filled in other one)

		_am->sourceMoteID = TOS_LOCAL_ADDRESS;
	    atomic _am->channel = samples[i].channel;

	    atomic reading = samples[i].reading;

    	switch(_am->channel) {
    		case SENSE_LIGHT: 
    			_am->sample = reading;
    			break;
    		case SENSE_ACCEL:
    			_am->sample = reading;
    			atomic _am->accelY = samples[i].accelY;
    			break;
    		case SENSE_BUTTON:
    			if (reading == 0)
    				_am->sample = 0;
    			else _am-> sample = ~0;
    			break;
    		case SENSE_TEMP:
    			_am->sample = reading;
    			break;
    		case SENSE_VOLTAGE:
    			_am->sample = reading;
    			break;
    		case SENSE_SOUND:
    			_am->sample = reading;
    			break;
    		default:
    			break;
    	}
 
    	if (call SendSampleMsg.send(TOS_BCAST_ADDR, sizeof(struct SampleMsg), &msg[lastMsg]) == SUCCESS) {
      		call Leds.redToggle();
    	}
    	else call Leds.yellowToggle();
  	}
  }
  
  event result_t SendSampleMsg.sendDone(TOS_MsgPtr sentMsg, result_t status) {
    call Leds.redToggle();
    return status;
  }

  /* StdControl *****************************************************************/
  command result_t StdControl.init() {
    call Leds.init();
    call CommControl.init();
    call Button.disable();
    call Button.clear();
    call Button.edge(TRUE); // Trigger interrupt on rising edge
    call Button.enable();
    atomic {
      state = READY;
      nextMsg = 0;
      nextSample = 0;
      am = (struct SampleMsg *)(msg[nextMsg].data); // point to first msg
    }    
    return SUCCESS;
  }
 
  command result_t StdControl.start() {
    call CommControl.start();
    // take samples at rate at which we send a mesg every sec
    call SampleTimer.start(TIMER_REPEAT,TIMER_RATE); 
    return SUCCESS;
  }

  command result_t StdControl.stop() {
    call CommControl.stop();
    call SampleTimer.stop();
    call Button.disable();
    call Button.clear();
    return SUCCESS;
  }
  
  /* Microphone *****************************************************************/  
  task void getSoundLevelData() {
    call Microphone.getData();
  }  

  async event result_t Microphone.dataReady(uint16_t data) {
    if (sampleSound) {
      atomic {
    	samples[nextSample].channel = SENSE_SOUND;
    	samples[nextSample].reading = data;
    	nextSample++;
      }
      post makeSendMsg();
    }
    return SUCCESS;
  }
 
  /* Internal voltage sensor ****************************************************/  
  task void getVoltageData() {
    call InternalVoltage.getData();
  }  

  async event result_t InternalVoltage.dataReady(uint16_t data) {
    if (sampleVoltage) {
      atomic {
    	samples[nextSample].channel = SENSE_VOLTAGE;
    	samples[nextSample].reading = data;
    	nextSample++;
      }
      post makeSendMsg();
    }
    post getSoundLevelData();
    return SUCCESS;
  }
  
  /* Temperature sensor *********************************************************/  
  task void getTempData() {
    call InternalTemp.getData();
  }  

  async event result_t InternalTemp.dataReady(uint16_t data) {
    if (sampleTemp) {
      atomic {
    	samples[nextSample].channel = SENSE_TEMP;
    	samples[nextSample].reading = data;
    	nextSample++;
      }
      post makeSendMsg();
    }
    post getVoltageData();
    return SUCCESS;
  }

  /* Button *********************************************************************/  
  task void getButtonData() {
  	if (sampleButton) {
  	  atomic {
  	    samples[nextSample].channel = SENSE_BUTTON;
  	    if (call Button.getValue()) samples[nextSample].reading = 0;
  	    else samples[nextSample].reading = ~0;
  	    nextSample++;
  	  }
  	  post makeSendMsg();
  	}
  	post getTempData();
  	return;
  }
  
  async event void Button.fired() {
    call Leds.yellowToggle();
    call Button.clear();
    return;
  }
  
  /* Light meter ****************************************************************/  
  task void getPhotoData() {
    call Photo.getData();
  }  

  async event result_t Photo.dataReady(uint16_t data) {
    if (sampleLight) {
      atomic {
    	samples[nextSample].channel = SENSE_LIGHT;
    	samples[nextSample].reading = data;
    	nextSample++;
      }
      post makeSendMsg();
    }
    post getButtonData();
    return SUCCESS;
  }
  
  /* Accelerometer **************************************************************/
  task void getAccelYData() {
    call AccelY.getData();
  }

  task void getAccelXData() {
    call AccelX.getData();
  }

  async event result_t AccelX.dataReady(uint16_t data) {
    if (sampleAccel) {
      atomic accelX = data; // TODO: FIX POTENTIAL RACE CONDITION
    }
    post getAccelYData();
    return SUCCESS;
  }

  async event result_t AccelY.dataReady(uint16_t data) {
    if (sampleAccel) {
      atomic {
        samples[nextSample].channel = SENSE_ACCEL;
        samples[nextSample].reading = accelX;
        samples[nextSample].accelY = data; // TODO: FIX POTENTIAL RACE CONDITION
        nextSample++;
      }
      post makeSendMsg();
    }
    post getPhotoData();
    return SUCCESS;
  }
  
  event result_t AccelControl.initDone() {
  	return SUCCESS;
  }

  event result_t AccelControl.startDone() {
  	return SUCCESS;
  }

  event result_t AccelControl.stopDone() {
  	return SUCCESS;
  }
 
  /* Sample timer. **************************************************************/
  event result_t SampleTimer.fired() {
    uint8_t counter;
    atomic counter = nextSample; // check how far we've got
    if (counter < (BUFFER_SIZE)) {
      post getAccelXData(); // other transducer readings daisy-chained to this
    }
    else {
      atomic {
		// reset counter
		nextSample = 0;
		// swap over msg buffer
		nextMsg ^= 0x1;
		am = (struct SampleMsg *)(msg[nextMsg].data);
      }
    }
    return SUCCESS;
  }
}
